#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""Web presentation Link, LinkType classes"""

#from django import template
#from django import http
from django.shortcuts import redirect
#      render_to_response, get_object_or_404, get_list_or_404, HttpResponse
from django.utils.translation import ugettext as _
#from django.utils.decorators import method_decorator
from django.utils.encoding import smart_unicode
#from django.views.decorators.cache import cache_control
#from django.views.decorators.cache import never_cache

#from django.views.decorators.csrf import csrf_protect
#from django.contrib.auth.decorators import login_required
from django.forms import models as model_forms
from django.forms import ModelForm, CharField

from django.contrib import messages
#from django.contrib.auth.models import Group
#from django.db import transaction
#from django.db.models import Q
#from copy import copy
#from django.views.generic import CreateView, UpdateView
#import datetime

from labman2.data.validators import validate_newltype
from labman2.data.views import (desktoplist, class_by_name)
#                                general_context, save_data_ondesktop,
#                                group_desktoplist, save_data_ingroups)
from labman2.data.dataviews import DataCreateView, DataUpdateView


#------------------------------------------------------------------------------
class ModelChoiceFieldReal(model_forms.ModelChoiceField):
    "Generate the labels of real objects."


    def label_from_instance(self, obj):

        return  smart_unicode(class_by_name(
                            obj.classname.classname).objects.get(id=obj.id))

#------------------------------------------------------------------------------
class LinkForm(ModelForm):
    'Generate form for Link model'


    class Meta(): # pylint: disable=C0111,C1001,W0232,R0903

        model = class_by_name('Link')

    new_link_type = CharField(max_length=70, required=False,
                                    validators=[validate_newltype],
                      help_text=_(u" If you want to create New Link Type, "
                                 "insert it's name here"))

    def clean(self):
        "Data validation"

        cleaned_data = self.cleaned_data
        link_type = cleaned_data.get("link_type")
        new_link_type = cleaned_data.get("new_link_type")
        subject = cleaned_data.get("subject")
        propert = cleaned_data.get("property")

        if not link_type:
            if not new_link_type:
                msg = _(u"At least one link type should be selected "
                      "(from LinkType or from New LinkType)")
                self._errors["link_type"] = self.error_class([msg])
                del cleaned_data["link_type"]
            else:
                if subject and propert and [elem for elem in
                      class_by_name('LinkType').objects.filter(
                                                classname1=subject.classname,
                                                classname2=propert.classname)
                                        if elem.link_type == new_link_type]:
                    msg = _(u"%s (%s - %s) link type exists and can be "
                            "selected from Link type list" % (new_link_type,
                                                subject.classname.classname,
                                                propert.classname.classname))
                    self._errors["link_type"] = self.error_class([msg])
                    del cleaned_data["link_type"]
                else:
                    msg = u"NEWLINKTYPE"
                    self._errors["link_type"] = self.error_class([msg])
                    del cleaned_data["link_type"]
        else:
            if subject:
                if cleaned_data['subject'].classname != cleaned_data[
                                                    'link_type'].classname1:
                    msg = _(u"Type of subject does not correspond to "
                            "that of link")
                    self._errors["subject"] = self.error_class([msg])
                    del cleaned_data["subject"]
            if propert:
                if cleaned_data['property'].classname != cleaned_data[
                                                    'link_type'].classname2:
                    msg = _(u"Type of property does not correspond to "
                            "that of link")
                    self._errors["property"] = self.error_class([msg])
                    del cleaned_data["property"]
            if new_link_type:
                msg = _(u"Only one link type should be selected "
                      "(from LinkType or from New LinkType)")
                self._errors["link_type"] = self.error_class([msg])
                del cleaned_data["link_type"]

            #nonidempotent
            if link_type.nonidempotent or not subject or not propert:
                pass
            #not nonidempotent+not commutative+not transitive
            elif not link_type.commutative and not link_type.transitive:
                if class_by_name('Link').objects.filter(subject=subject,
                                                   property=propert,
                                            link_type=link_type).exists():
                    msg = _(u"Link with this Subject, Property and LinkType "
                        "already exists.")
                    self._errors["subject"] = self.error_class([msg])
                    del cleaned_data["subject"]
            #not nonidempotent+commutative+not transitive
            elif link_type.commutative and not link_type.transitive:
                if (class_by_name('Link').objects.filter(subject=propert,
                                                    property=subject,
                                           link_type=link_type).exists() or
                    class_by_name('Link').objects.filter(subject=subject,
                                                   property=propert,
                                            link_type=link_type).exists()):
                    msg = _(u"LinkType is comutative. Existing links "
                        "conflict with this Subject/Property pair.")
                    self._errors["subject"] = self.error_class([msg])
                    del cleaned_data["subject"]
            #not nonidempotent+commutative+transitive
            elif link_type.commutative and link_type.transitive:
                result1 = class_by_name('Link').objects.all_transcomm(
                                                    link_type.id, subject.id)
                result2 = class_by_name('Link').objects.all_transcomm(
                                                    link_type.id, propert.id)
                if (subject.id in result2) and (propert.id in result1):
                    msg = _(u"LinkType is comutative and transitive. Existing "
                        "links conflict with this Subject/Property pair.")
                    self._errors["subject"] = self.error_class([msg])
                    del cleaned_data["subject"]
            #not nonidempotent+not commutative+transitive
            elif not link_type.commutative and link_type.transitive:
                des_result = class_by_name('Link').objects.descendants(
                                                    link_type.id, subject.id)
                asc_result = class_by_name('Link').objects.ascendants(
                                                    link_type.id, propert.id)
                if (propert.id in des_result) or (subject.id in asc_result):
                    msg = _(u"LinkType is transitive. Existing "
                        "links conflict with this Subject/Property pair.")
                    self._errors["subject"] = self.error_class([msg])
                    del cleaned_data["subject"]
        return cleaned_data

#------------------------------------------------------------------------------
class LinkCreateView(DataCreateView):
    'Generate forms for creating new data items'


    def get_initial(self):
        "Initial data getting from path parameters"

        initial = {}
        if (self.kwargs.has_key('subjid') and
            self.kwargs.has_key('properid') and
            self.kwargs.has_key('ltypeid')):
            if self.kwargs['subjid']:
                sub = class_by_name('Base').objects.get(
                                                    pk=self.kwargs['subjid'])
                initial['subject'] = class_by_name(
                                    sub.classname.classname).objects.get(
                                                    pk=self.kwargs['subjid'])
            if self.kwargs['properid']:
                prp = class_by_name('Base').objects.get(
                                                    pk=self.kwargs['properid'])
                initial['property'] = class_by_name(
                                    prp.classname.classname).objects.get(
                                                    pk=self.kwargs['properid'])
            if self.kwargs['ltypeid']:
                initial['link_type'] = class_by_name('LinkType').objects.get(
                                                    pk=self.kwargs['ltypeid'])
        return initial


    def get_queryset(self):
        'Make a gueryset for subjet and property fields'

        sel = {}
        #elem on the desktop
        base_pk = [elem[0].base_id for elem in
                            desktoplist(self.request)] # pylint: disable=E1101
        sel['base_select'] = class_by_name('Base').objects.filter(
                                                                id__in=base_pk)
        sel['linktype_select'] = class_by_name('LinkType').objects.filter(
                                                            id__in=base_pk)
        return sel

    def get_form_class(self):
        'Make a form from a class'

        form = LinkForm
        #elem on the desktop
        selection = self.get_queryset()
        form.base_fields[ # pylint: disable=E1101
                         'subject'] = ModelChoiceFieldReal(
                                                      selection['base_select'],
                                                      required=False)
        form.base_fields[ # pylint: disable=E1101
                        'property'] = ModelChoiceFieldReal(
                                                      selection['base_select'],
                                                      required=False)
        form.base_fields['link_type'] = ModelChoiceFieldReal(
                                                  selection['linktype_select'],
                                                  required=False)
        return form

    def form_invalid(self, form):
        """Trap for especial case link_type field is empty and it should be
           caught before db querying.
        """

        if form._errors.has_key('link_type') and \
                form._errors['link_type'].__repr__() == repr([u'NEWLINKTYPE']):
            subjid = self.request.POST['subject'] # pylint: disable=E1101
            properid = self.request.POST['property'] # pylint: disable=E1101
            ltypename = self.request.POST[ # pylint: disable=E1101
                                           'new_link_type']
            if ltypename:
                return redirect(u"/add/LinkType/%s/%s/%s/"
                                % (subjid, properid, ltypename))
            else:
                del form._errors['link_type']
        return self.render_to_response( # pylint: disable=E1101
                      self.get_context_data(form=form)) # pylint: disable=E1101


#------------------------------------------------------------------------------
class LinkUpdateView(DataUpdateView):
    'Link form for editing links.'


    template_name = 'add_data.html'

    def get_queryset(self):
        'Make a gueryset for subjet and property fields'

        sel = {}
        base_pk = [elem[0].base_id for elem in
                            desktoplist(self.request)] # pylint: disable=E1101
        sel['base_select'] = class_by_name('Base').objects.filter(
                                                                id__in=base_pk)
        sel['linktype_select'] = class_by_name('LinkType').objects.filter(
                                                            id__in=base_pk)
        return sel

    def get_form_class(self):
        'Make a form from a class'

        form = LinkForm
        selection = self.get_queryset()
        form.base_fields[ # pylint: disable=E1101
                         'subject'] = ModelChoiceFieldReal(
                                                      selection['base_select'],
                                                      required=False)
        if self.object.subject and not (self.object.subject in
                                                    selection['base_select']):
            real_obj = class_by_name(
                    self.object.subject.classname.classname).objects.get(
                                                    id=self.object.subject.id)
            form.base_fields[
                             'subject'].help_text = _("Subject %s not on the "
                                                      "desktop" %real_obj)

        form.base_fields[ # pylint: disable=E1101
                        'property'] = ModelChoiceFieldReal(
                                                      selection['base_select'],
                                                      required=False)
        if self.object.property and not (self.object.property in
                                                    selection['base_select']):
            real_obj = class_by_name(
                    self.object.property.classname.classname).objects.get(
                                                    id=self.object.property.id)
            form.base_fields[
                            'property'].help_text = _("Property %s not on the "
                                                        "desktop" %real_obj)
        form.base_fields['link_type'] = ModelChoiceFieldReal(
                                                  selection['linktype_select'],
                                                  required=False)
        if self.object.link_type and not (self.object.link_type in
                                                selection['linktype_select']):
            form.base_fields[
                            'link_type'].help_text = _("Link type %s not "
                                       "on the desktop" %self.object.link_type)

        return form

    def form_invalid(self, form):
        """Trap for especial case link_type field is empty and it should be
           caught before db querying.
        """

        if form._errors.has_key('link_type') and \
                form._errors['link_type'].__repr__() == repr([u'NEWLINKTYPE']):
            subjid = self.request.POST['subject'] # pylint: disable=E1101
            properid = self.request.POST['property'] # pylint: disable=E1101
            ltypename = self.request.POST[ # pylint: disable=E1101
                                           'new_link_type']
            if ltypename:
                return redirect(u"/add/LinkType/%s/%s/%s/"
                                % (subjid, properid, ltypename))
            else:
                del form._errors['link_type']
        return self.render_to_response( # pylint: disable=E1101
                      self.get_context_data(form=form)) # pylint: disable=E1101

#------------------------------------------------------------------------------
class LinkTypeCreateView(DataCreateView):
    'Generate forms for creating new data items'


    def get_initial(self):

        initial = {}
        if (self.kwargs.has_key('subjid') and
            self.kwargs.has_key('properid') and
            self.kwargs.has_key('ltypename')):
            if self.kwargs['ltypename']:
                initial['link_type'] = self.kwargs['ltypename']
            if self.kwargs['subjid']:
                initial['classname1'] = (class_by_name('Base').objects.get(
                                        pk=self.kwargs['subjid'])).classname
            if self.kwargs['properid']:
                initial['classname2'] = (class_by_name('Base').objects.get(
                                        pk=self.kwargs['properid'])).classname
        return initial


    def get_success_url(self):
        if self.object:
            url = '/add/Link/%s/%s/%s/' % (self.kwargs['subjid'],
                                            self.kwargs['properid'],
                                            self.object.id)
        else:
            url = '/add/LinkType/%s/%s/%s/' % (self.kwargs['subjid'],
                                            self.kwargs['properid'],
                                            self.kwargs['ltypename'])
        return url

#------------------------------------------------------------------------------
class SubPropCreateView(DataCreateView):
    """Generate subject/property forms for creating new data items and
       return to the edit link page.
    """


    def get_success_url(self):
        'Prepare the next page url. '

        if self.object:
            messages.add_message(self.request, messages.SUCCESS,
              _('Successfully saved %s Data you can add as subject/property'
                ' in Link data' % self.object))
            url = '/edit/Link/%s/' % (self.kwargs['tolink'])
        else:
            url = '/add/%s/%s/' % (self.kwargs['fmname'], self.kwargs['tolink'])
        return url
